/*
 * $Id: Project.java,v 1.11 2003/06/01 11:23:32 znamenacek Exp $
 *
 * Copyright (c) Tomas Znamenacek
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

package net.sf.vofce.project;

import org.apache.log4j.Category;

import net.sf.vofce.guts.Function;
import net.sf.vofce.util.NiceProperties;

import java.util.Vector;
import java.util.Iterator;
import java.util.List;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.ListModel;

/**
 * Takes care of a collection of functions.
 * 
 * @author Tomas Znamenacek
 */
public class Project implements ListModel {

	/** Stamp saved with the functions. */
	public static final String SAVE_STAMP = "saved by vofce, see http://sf.net/projects/vofce/ for more information";
	
	/** Logger instance. */
	protected static Category logger = Category.getRoot();

	/** The only instance of <code>Project</code> class. */
	protected static Project instance = new Project();

	/** List of all the functions. */
	protected Vector functions = new Vector();

	/**
	 * Listeners that wish to receive information about changes
	 * in the function collection.
	 */
	private List functionListeners;

	/**
	 * Listeners that wish to receive information about changes
	 * in the project.
	 * Changes in the project include function set changes etc.
	 */
	private List eventListeners;
	
	/**
	 * Hash of various information that other parts of the application
	 * wish to save with the project.
	 */
	protected NiceProperties projectSettings = new NiceProperties();

	protected boolean dirty = false;
	
	/** Returns <code>Project</code> instance. */
	public static Project getInstance() {

		return instance;
	}

	/** Creates <code>Project</code> instance. */
	private Project() {

		functionListeners = new Vector();
		eventListeners = new Vector();
	}

	//
	// List persistence operations
	//

	public boolean isDirty() {

		return dirty;
	}
	
	/**
	 * Stores the function list into stream.
	 * Includes other settings that may have been set by other
	 * application parts.
	 */
	public void store(OutputStream output) throws IOException {

		fireSavingProject();


		for (int i = 0; i < functions.size(); i++) {

			projectSettings.setProperty("function." + Integer.toString(i),
				((Function) functions.elementAt(i)).toString());
		}

		projectSettings.setProperty("function.number", Integer.toString(functions.size()));
		projectSettings.store(output, SAVE_STAMP);
		dirty = false;
	}

	/**
	 * Retrieves the function list from a stream.
	 * Retrieves also other settings that may have been saved with
	 * the functions. Throws away the current content of properties.
	 */
	public void load(InputStream input) throws IOException {
	
		projectSettings.clear();
		projectSettings.load(input);

		removeAllFunctions();

		int total = projectSettings.getInt("function.number", 0);
		for (int i = 0; i < total; i++) {

			try {
				Function loaded = new Function(projectSettings.getString("function." + i, "<function missing>"));
				addFunction(loaded);
			} catch (Exception e) {
				logger.warn("Project: Can't load function " + i + " from file.");
			}

		}

		fireProjectLoaded();
		dirty = false;
	}

	public void clear() {

		dirty = false;
		projectSettings.clear();
		removeAllFunctions();
		fireProjectCleared();
	}
	
	//
	// List manipulation
	//
	
	/** Adds function to the list. */
	public void addFunction(Function f) {

		functions.add(f);
		int index = functions.size() - 1;

		ListDataEvent foo = new ListDataEvent(functions, ListDataEvent.INTERVAL_ADDED, index, index);
		Iterator i = functionListeners.iterator();		
	
		while (i.hasNext()) {
					
			((ListDataListener) i.next()).intervalAdded(foo);
		}

		dirty = true;
		fireFunctionSetChanged();
	}

	/** Remove function from the list. */
	public void removeFunction(int index) {
		
		functions.remove(index);

		ListDataEvent foo = new ListDataEvent(functions, ListDataEvent.INTERVAL_REMOVED, index, index);
		Iterator i = functionListeners.iterator();		
	
		while (i.hasNext()) {
					
			((ListDataListener) i.next()).intervalRemoved(foo);
		}

		dirty = true;
		fireFunctionSetChanged();
	}

	/** Change function at given position. */
	public void changeFunction(int index, Function f) {

		functions.set(index, f);
		
		ListDataEvent foo = new ListDataEvent(functions, ListDataEvent.CONTENTS_CHANGED, index, index);
		Iterator i = functionListeners.iterator();		
	
		while (i.hasNext()) {
					
			((ListDataListener) i.next()).contentsChanged(foo);
		}

		dirty = true;
		fireFunctionSetChanged();
	}
	
	/** Deletes all functions from the list. */
	public void removeAllFunctions() {
	
		if (functions.size() == 0) { return; }
		
		ListDataEvent foo = new ListDataEvent(functions, ListDataEvent.INTERVAL_REMOVED, 0, functions.size()-1);
		functions.removeAllElements();
		Iterator i = functionListeners.iterator();		
	
		while (i.hasNext()) {
					
			((ListDataListener) i.next()).intervalRemoved(foo);
		}

		fireFunctionSetChanged();
	}

	/** Returns a function. */
	public Function getFunction(int index) {
		
		return (Function) functions.elementAt(index);
	}
	
	/** Returns all the functions in an iterator. */
	public Iterator getIterator() {
		
		return functions.iterator();
	}

	/** Returns an array of all the functions. */
	public Function[] getFunctions() {

		return (Function[]) functions.toArray(new Function[] {});
	}

	//
	// Methods implementing ListModel
	//

	public Object getElementAt(int index) {
		return ((Function) functions.elementAt(index)).getUserView();
	}

	public int getSize() {
		return functions.size();
	}

	public void addListDataListener(ListDataListener l) {
		functionListeners.add(l);
	}

	public void removeListDataListener(ListDataListener l) {
		functionListeners.remove(l);
	}

	//
	// ProjectEventListener agenda
	// 
	
	public void addEventListener(ProjectEventListener l) {

		eventListeners.add(l);
	}

	public void removeEventListener(ProjectEventListener l) {

		eventListeners.remove(l);
	}

	protected void fireFunctionSetChanged() {

		Iterator i = eventListeners.iterator();

		while (i.hasNext()) {

			((ProjectEventListener) i.next()).functionSetChanged();
		}
	}

	protected void fireSavingProject() {

		Iterator i = eventListeners.iterator();

		while (i.hasNext()) {

			((ProjectEventListener) i.next()).savingProject(projectSettings);
		}
	}
	
	protected void fireProjectLoaded() {

		Iterator i = eventListeners.iterator();

		while (i.hasNext()) {

			((ProjectEventListener) i.next()).projectLoaded(projectSettings);
		}
	}

	protected void fireProjectCleared() {

		Iterator i = eventListeners.iterator();

		while (i.hasNext()) {

			((ProjectEventListener) i.next()).projectCleared();
		}
	}
}
